//===--- ASTScopeLookup.cpp - Swift Object-Oriented AST Scope -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// This file implements the lookup functionality of the AstScopeImpl ontology.
///
//===----------------------------------------------------------------------===//

#include "polarphp/ast/AstScope.h"
#include "polarphp/ast/AstContext.h"
#include "polarphp/ast/AstWalker.h"
#include "polarphp/ast/Decl.h"
#include "polarphp/ast/Expr.h"
#include "polarphp/ast/Initializer.h"
#include "polarphp/ast/LazyResolver.h"
#include "polarphp/ast/Module.h"
#include "polarphp/ast/NameLookup.h"
#include "polarphp/ast/ParameterList.h"
#include "polarphp/ast/Pattern.h"
#include "polarphp/ast/SourceFile.h"
#include "polarphp/ast/Stmt.h"
#include "polarphp/ast/TypeRepr.h"
#include "polarphp/basic/StlExtras.h"
#include "llvm/Support/Compiler.h"
#include <algorithm>

using namespace polar;
using namespace namelookup;
using namespace ast_scope;

static bool isLocWithinAnInactiveClause(const SourceLoc loc, SourceFile *SF);

llvm::SmallVector<const AstScopeImpl *, 0> AstScopeImpl::unqualifiedLookup(
   SourceFile *sourceFile, const DeclName name, const SourceLoc loc,
   const DeclContext *const startingContext, DeclConsumer consumer) {
   SmallVector<const AstScopeImpl *, 0> history;
   const auto *start =
      findStartingScopeForLookup(sourceFile, name, loc, startingContext);
   if (start)
      start->lookup(history, nullptr, nullptr, consumer);
   return history;
}

const AstScopeImpl *AstScopeImpl::findStartingScopeForLookup(
   SourceFile *sourceFile, const DeclName name, const SourceLoc loc,
   const DeclContext *const startingContext) {
   // At present, use legacy code in unqualifiedLookup.cpp to handle module-level
   // lookups
   // TODO: implement module scope someday
   if (startingContext->getContextKind() == DeclContextKind::Module)
      return nullptr;

   auto *const fileScope = sourceFile->getScope().impl;
   // Parser may have added decls to source file, since previous lookup
   if (name.isOperator())
      return fileScope; // operators always at file scope

   const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr);
   ast_scope_assert(innermost->getWasExpanded(),
                    "If looking in a scope, it must have been expanded.");

   // The legacy lookup code gets passed both a SourceLoc and a starting context.
   // However, our ultimate intent is for clients to not have to pass in a
   // DeclContext at all, since the SourceLoc should be enough. While we are
   // debugging the new ASTScope lookup code, we can catch bugs by comparing the
   // DeclContext of the ASTScope found from the desired SourceLoc to the
   // DeclContext passed in by the client.

   const auto *startingScope = innermost;
   for (; startingScope &&
          !startingScope->doesContextMatchStartingContext(startingContext);
          startingScope = startingScope->getParent().getPtrOrNull()) {
   }
   // Someday, just use the assertion below. For now, print out lots of info for
   // debugging.
   if (!startingScope) {
      llvm::errs() << "AstScopeImpl: resorting to startingScope hack, file: "
                   << sourceFile->getFilename() << "\n";
      // The check is costly, and inactive lookups will end up here, so don't
      // do the check unless we can't find the startingScope.
      const bool isInInactiveClause =
         isLocWithinAnInactiveClause(loc, sourceFile);
      if (isInInactiveClause)
         llvm::errs() << "  because location is within an inactive clause\n";
      llvm::errs() << "'";
      name.print(llvm::errs());
      llvm::errs() << "' ";
      llvm::errs() << "loc: ";
      loc.print(llvm::errs(), sourceFile->getAstContext().SourceMgr);
      llvm::errs() << "\nstarting context:\n ";
      startingContext->printContext(llvm::errs());
      //    llvm::errs() << "\ninnermost: ";
      //    innermost->dump();
      //    llvm::errs() << "in: \n";
      //    fileScope->dump();
      llvm::errs() << "\n\n";

      // Might distort things
      //    if (fileScope->crossCheckWithAST())
      //      llvm::errs() << "Tree creation missed some DeclContexts.\n";

      // Crash compilation even if NDEBUG
      if (isInInactiveClause)
         llvm::report_fatal_error(
            "A lookup was attempted into an inactive clause");
   }

   ast_scope_assert(startingScope, "AstScopeImpl: could not find startingScope");
   return startingScope;
}

AstScopeImpl *
AstScopeImpl::findInnermostEnclosingScope(SourceLoc loc,
                                          NullablePtr<raw_ostream> os) {
   return findInnermostEnclosingScopeImpl(loc, os, getSourceManager(),
                                          getScopeCreator());
}

AstScopeImpl *AstScopeImpl::findInnermostEnclosingScopeImpl(
   SourceLoc loc, NullablePtr<raw_ostream> os, SourceManager &sourceMgr,
   ScopeCreator &scopeCreator) {
   expandAndBeCurrentDetectingRecursion(scopeCreator);
   auto child = findChildContaining(loc, sourceMgr);
   if (!child)
      return this;
   return child.get()->findInnermostEnclosingScopeImpl(loc, os, sourceMgr,
                                                       scopeCreator);
}

bool AstScopeImpl::checkSourceRangeOfThisASTNode() const {
   const auto r = getSourceRangeOfThisAstNode();
   (void)r;
   ast_scope_assert(!getSourceManager().isBeforeInBuffer(r.end, r.start),
                    "Range is backwards.");
   return true;
}

NullablePtr<AstScopeImpl>
AstScopeImpl::findChildContaining(SourceLoc loc,
                                  SourceManager &sourceMgr) const {
   // Use binary search to find the child that contains this location.
   struct CompareLocs {
      SourceManager &sourceMgr;

      bool operator()(const AstScopeImpl *scope, SourceLoc loc) {
         ast_scope_assert(scope->checkSourceRangeOfThisASTNode(), "Bad range.");
         return -1 == AstScopeImpl::compare(scope->getSourceRangeOfScope(), loc,
                                            sourceMgr,
            /*ensureDisjoint=*/false);
      }
      bool operator()(SourceLoc loc, const AstScopeImpl *scope) {
         ast_scope_assert(scope->checkSourceRangeOfThisASTNode(), "Bad range.");
         // Alternatively, we could check that loc < start-of-scope
         return 0 >= AstScopeImpl::compare(loc, scope->getSourceRangeOfScope(),
                                           sourceMgr,
            /*ensureDisjoint=*/false);
      }
   };
   auto *const *child = std::lower_bound(
      getChildren().begin(), getChildren().end(), loc, CompareLocs{sourceMgr});

   if (child != getChildren().end() &&
       sourceMgr.rangeContainsTokenLoc((*child)->getSourceRangeOfScope(), loc))
      return *child;

   return nullptr;
}

#pragma mark doesContextMatchStartingContext
// Match existing UnqualifiedLookupBehavior

bool AstScopeImpl::doesContextMatchStartingContext(
   const DeclContext *context) const {
   // Why are we not checking the loc for this--because already did binary search
   // on loc to find the start First, try MY DeclContext
   if (auto myDCForL = getDeclContext())
      return myDCForL == context;
   // If I don't have one, ask my parent.
   // (Choose innermost scope with matching loc & context.)
   if (auto p = getParent())
      return p.get()->doesContextMatchStartingContext(context);
   // Topmost scope always has a context, the SourceFile.
   ast_scope_unreachable("topmost scope always has a context, the SourceFile");
}

// For a SubscriptDecl with generic parameters, the call tries to do lookups
// with startingContext equal to either the get or set subscript
// AbstractFunctionDecls. Since the generic parameters are in the
// SubScriptDeclScope, and not the AbstractFunctionDecl scopes (after all how
// could one parameter be in two scopes?), GenericParamScoped intercepts the
// match query here and tests against the accessor DeclContexts.
bool GenericParamScope::doesContextMatchStartingContext(
   const DeclContext *context) const {
   if (auto *asd = dyn_cast<AbstractStorageDecl>(holder)) {
      for (auto accessor : asd->getAllAccessors()) {
         if (up_cast<DeclContext>(accessor) == context)
            return true;
      }
   }
   return false;
}

#pragma mark lookup methods that run once per scope

void AstScopeImpl::lookup(SmallVectorImpl<const AstScopeImpl *> &history,
                          const NullablePtr<const AstScopeImpl> limit,
                          NullablePtr<const GenericParamList> lastListSearched,
                          DeclConsumer consumer) const {

   history.push_back(this);

#ifndef NDEBUG
   consumer.startingNextLookupStep();
#endif

   // Certain illegal nestings, e.g. protocol nestled inside a struct,
   // require that lookup stop at the outer scope.
   if (this == limit.getPtrOrNull()) {
#ifndef NDEBUG
      consumer.finishingLookup("limit return");
#endif
      return;
   }

   // Look for generics before members in violation of lexical ordering because
   // you can say "self.name" to get a name shadowed by a generic but you
   // can't do the opposite to get a generic shadowed by a name.
   const auto doneAndListSearched =
      lookInMyGenericParameters(lastListSearched, consumer);
   if (doneAndListSearched.first)
      return;

   if (lookupLocalsOrMembers(history, consumer))
      return;

   const auto *const lookupParent = getLookupParent().getPtrOrNull();
   if (!lookupParent) {
#ifndef NDEBUG
      consumer.finishingLookup("Finished lookup; no parent");
#endif
      return;
   }

   // If there is no limit and this scope induces one, pass that on.
   const NullablePtr<const AstScopeImpl> limitForParent =
      limit ? limit : getLookupLimit();

   return lookupParent->lookup(history, limitForParent, lastListSearched,
                               consumer);
}

#pragma mark genericParams()

NullablePtr<const GenericParamList> AstScopeImpl::genericParams() const {
   return nullptr;
}
NullablePtr<const GenericParamList>
AbstractFunctionDeclScope::genericParams() const {
   return decl->getGenericParams();
}
NullablePtr<const GenericParamList> SubscriptDeclScope::genericParams() const {
   return decl->getGenericParams();
}
NullablePtr<const GenericParamList> GenericTypeScope::genericParams() const {
   // For Decls:
   // WAIT, WHAT?! Isn't this covered by the GenericParamScope
   // lookupLocalsOrMembers? No, that's for use of generics in the body. This is
   // for generic restrictions.

   // For Bodies:
   // Sigh... These must be here so that from body, we search generics before
   // members. But they also must be on the Decl scope for lookups starting from
   // generic parameters, where clauses, etc.
   return getGenericContext()->getGenericParams();
}
NullablePtr<const GenericParamList> ExtensionScope::genericParams() const {
   return decl->getGenericParams();
}

#pragma mark lookInMyGenericParameters

std::pair<bool, NullablePtr<const GenericParamList>>
AstScopeImpl::lookInMyGenericParameters(
   NullablePtr<const GenericParamList> formerListSearched,
   AstScopeImpl::DeclConsumer consumer) const {
   auto listToSearch = genericParams();
   if (listToSearch == formerListSearched)
      return std::make_pair(false, formerListSearched);

   // For extensions of nested types, must check outer parameters
   for (auto *params = listToSearch.getPtrOrNull(); params;
        params = params->getOuterParameters()) {
      if (lookInGenericParametersOf(params, consumer))
         return std::make_pair(true, listToSearch);
   }
   return std::make_pair(false, listToSearch);
}

bool AstScopeImpl::lookInGenericParametersOf(
   const NullablePtr<const GenericParamList> paramList,
   AstScopeImpl::DeclConsumer consumer) {
   if (!paramList)
      return false;
   SmallVector<ValueDecl *, 32> bindings;
   for (auto *param : paramList.get()->getParams())
      bindings.push_back(param);
   if (consumer.consume(bindings, DeclVisibilityKind::GenericParameter))
      return true;
   return false;
}

#pragma mark looking in locals or members - members

bool AstScopeImpl::lookupLocalsOrMembers(ArrayRef<const AstScopeImpl *>,
                                         DeclConsumer) const {
   return false; // many kinds of scopes have none
}

bool GenericTypeOrExtensionScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *> history,
   AstScopeImpl::DeclConsumer consumer) const {
   // isCascadingUseArg must have already been resolved, for a real lookup
   // but may be \c None for dumping.
   return portion->lookupMembersOf(this, history, consumer);
}

bool Portion::lookupMembersOf(const GenericTypeOrExtensionScope *,
                              ArrayRef<const AstScopeImpl *>,
                              AstScopeImpl::DeclConsumer) const {
   return false;
}

bool GenericTypeOrExtensionWhereOrBodyPortion::lookupMembersOf(
   const GenericTypeOrExtensionScope *scope,
   ArrayRef<const AstScopeImpl *> history,
   AstScopeImpl::DeclConsumer consumer) const {
   auto nt = scope->getCorrespondingNominalTypeDecl().getPtrOrNull();
   if (!nt)
      return false;
   auto selfDC = computeSelfDC(history);
   return consumer.lookInMembers(selfDC, scope->getDeclContext().get(), nt,
                                 [&](Optional<bool> initialIsCascadingUse) {
                                    return AstScopeImpl::computeIsCascadingUse(
                                       history, initialIsCascadingUse)
                                       .getValueOr(true);
                                 });
}

#pragma mark looking in locals or members - locals

bool GenericParamScope::lookupLocalsOrMembers(ArrayRef<const AstScopeImpl *>,
                                              DeclConsumer consumer) const {
   auto *param = paramList->getParams()[index];
   return consumer.consume({param}, DeclVisibilityKind::GenericParameter);
}

bool PatternEntryDeclScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *>, DeclConsumer consumer) const {
   if (vis != DeclVisibilityKind::LocalVariable)
      return false; // look in self type will find this later
   return lookupLocalBindingsInPattern(getPattern(), vis, consumer);
}

bool ForEachPatternScope::lookupLocalsOrMembers(ArrayRef<const AstScopeImpl *>,
                                                DeclConsumer consumer) const {
   return lookupLocalBindingsInPattern(
      stmt->getPattern(), DeclVisibilityKind::LocalVariable, consumer);
}

bool CatchStmtScope::lookupLocalsOrMembers(ArrayRef<const AstScopeImpl *>,
                                           DeclConsumer consumer) const {
   return lookupLocalBindingsInPattern(
      stmt->getErrorPattern(), DeclVisibilityKind::LocalVariable, consumer);
}

bool CaseStmtScope::lookupLocalsOrMembers(ArrayRef<const AstScopeImpl *>,
                                          DeclConsumer consumer) const {
   for (auto &item : stmt->getMutableCaseLabelItems())
      if (lookupLocalBindingsInPattern(
         item.getPattern(), DeclVisibilityKind::LocalVariable, consumer))
         return true;
   return false;
}

bool AbstractFunctionBodyScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *>, DeclConsumer consumer) const {
   if (auto *paramList = decl->getParameters()) {
      for (auto *paramDecl : *paramList)
         if (consumer.consume({paramDecl}, DeclVisibilityKind::FunctionParameter))
            return true;
   }
   return false;
}

bool MethodBodyScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *> history, DeclConsumer consumer) const {
   ast_scope_assert(isAMethod(decl), "Asking for members of a non-method.");
   if (AbstractFunctionBodyScope::lookupLocalsOrMembers(history, consumer))
      return true;
   return consumer.consume({decl->getImplicitSelfDecl()},
                           DeclVisibilityKind::FunctionParameter);
}

bool PureFunctionBodyScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *> history, DeclConsumer consumer) const {
   ast_scope_assert(
      !isAMethod(decl),
      "Should have called lookupLocalsOrMembers instead of this function.");
   if (AbstractFunctionBodyScope::lookupLocalsOrMembers(history, consumer))
      return true;

   // Consider \c var t: T { (did/will/)get/set { ... t }}
   // Lookup needs to find t, but if the var is inside of a type the baseDC needs
   // to be set. It all works fine, except: if the var is not inside of a type,
   // then t needs to be found as a local binding:
   if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
      if (auto *storage = accessor->getStorage())
         if (consumer.consume({storage}, DeclVisibilityKind::LocalVariable))
            return true;
   }
   return false;
}

bool SpecializeAttributeScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *>, DeclConsumer consumer) const {
   if (auto *params = whatWasSpecialized->getGenericParams())
      for (auto *param : params->getParams())
         if (consumer.consume({param}, DeclVisibilityKind::GenericParameter))
            return true;
   return false;
}

bool BraceStmtScope::lookupLocalsOrMembers(ArrayRef<const AstScopeImpl *>,
                                           DeclConsumer consumer) const {
   // All types and functions are visible anywhere within a brace statement
   // scope. When ordering matters (i.e. var decl) we will have split the brace
   // statement into nested scopes.
   //
   // Don't stop at the first one, there may be local funcs with same base name
   // and want them all.
   SmallVector<ValueDecl *, 32> localBindings;
   for (auto braceElement : stmt->getElements()) {
      if (auto localBinding = braceElement.dyn_cast<Decl *>()) {
         if (auto *vd = dyn_cast<ValueDecl>(localBinding))
            localBindings.push_back(vd);
      }
   }
   return consumer.consume(localBindings, DeclVisibilityKind::LocalVariable);
}

bool PatternEntryInitializerScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *>, DeclConsumer consumer) const {
   // 'self' is available within the pattern initializer of a 'lazy' variable.
   auto *initContext = cast_or_null<PatternBindingInitializer>(
      decl->getInitContext(0));
   if (initContext) {
      if (auto *selfParam = initContext->getImplicitSelfDecl()) {
         return consumer.consume({selfParam},
                                 DeclVisibilityKind::FunctionParameter);
      }
   }
   return false;
}

bool ClosureParametersScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *>, DeclConsumer consumer) const {
   if (auto *cl = captureList.getPtrOrNull()) {
      CaptureListExpr *mutableCL =
         const_cast<CaptureListExpr *>(captureList.get());
      for (auto &e : mutableCL->getCaptureList()) {
         if (consumer.consume(
            {e.Var},
            DeclVisibilityKind::LocalVariable)) // or FunctionParameter??
            return true;
      }
   }
   for (auto param : *closureExpr->getParameters())
      if (consumer.consume({param}, DeclVisibilityKind::FunctionParameter))
         return true;
   return false;
}

bool ConditionalClausePatternUseScope::lookupLocalsOrMembers(
   ArrayRef<const AstScopeImpl *>, DeclConsumer consumer) const {
   return lookupLocalBindingsInPattern(
      pattern, DeclVisibilityKind::LocalVariable, consumer);
}

bool AstScopeImpl::lookupLocalBindingsInPattern(Pattern *p,
                                                DeclVisibilityKind vis,
                                                DeclConsumer consumer) {
   if (!p)
      return false;
   bool isDone = false;
   p->forEachVariable([&](VarDecl *var) {
      if (!isDone)
         isDone = consumer.consume({var}, vis);
   });
   return isDone;
}

#pragma mark computeSelfDC

NullablePtr<DeclContext>
GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC(
   ArrayRef<const AstScopeImpl *> history) {
   ast_scope_assert(history.size() != 0, "includes current scope");
   size_t i = history.size() - 1; // skip last entry (this scope)
   while (i != 0) {
      Optional<NullablePtr<DeclContext>> maybeSelfDC =
         history[--i]->computeSelfDCForParent();
      if (maybeSelfDC)
         return *maybeSelfDC;
   }
   return nullptr;
}

#pragma mark compute isCascadingUse

Optional<bool> AstScopeImpl::computeIsCascadingUse(
   ArrayRef<const AstScopeImpl *> history,
   const Optional<bool> initialIsCascadingUse) {
   Optional<bool> isCascadingUse = initialIsCascadingUse;
   for (const auto *scope : history)
      isCascadingUse = scope->resolveIsCascadingUseForThisScope(isCascadingUse);
   return isCascadingUse;
}

#pragma mark getLookupLimit

NullablePtr<const AstScopeImpl> AstScopeImpl::getLookupLimit() const {
   return nullptr;
}

NullablePtr<const AstScopeImpl>
GenericTypeOrExtensionScope::getLookupLimit() const {
   return portion->getLookupLimitFor(this);
}

NullablePtr<const AstScopeImpl>
Portion::getLookupLimitFor(const GenericTypeOrExtensionScope *) const {
   return nullptr;
}
NullablePtr<const AstScopeImpl>
GenericTypeOrExtensionWholePortion::getLookupLimitFor(
   const GenericTypeOrExtensionScope *scope) const {
   return scope->getLookupLimitForDecl();
}

NullablePtr<const AstScopeImpl>
GenericTypeOrExtensionScope::getLookupLimitForDecl() const {
   return nullptr;
}

NullablePtr<const AstScopeImpl>
NominalTypeScope::getLookupLimitForDecl() const {
   if (isa<InterfaceDecl>(decl)) {
      // InterfaceDecl can only be legally nested in a SourceFile,
      // so any other kind of Decl is illegal
      return parentIfNotChildOfTopScope();
   }
   // AFAICT, a struct, decl, or enum can be nested inside anything
   // but a InterfaceDecl.
   return ancestorWithDeclSatisfying(
      [&](const Decl *const d) { return isa<InterfaceDecl>(d); });
}

NullablePtr<const AstScopeImpl> ExtensionScope::getLookupLimitForDecl() const {
   // Extensions can only be legally nested in a SourceFile,
   // so any other kind of Decl is illegal
   return parentIfNotChildOfTopScope();
}

NullablePtr<const AstScopeImpl> AstScopeImpl::ancestorWithDeclSatisfying(
   function_ref<bool(const Decl *)> predicate) const {
   for (NullablePtr<const AstScopeImpl> s = getParent(); s;
        s = s.get()->getParent()) {
      if (Decl *d = s.get()->getDeclIfAny().getPtrOrNull()) {
         if (predicate(d))
            return s;
      }
   }
   return nullptr;
}

#pragma mark computeSelfDCForParent

// If the lookup depends on implicit self, selfDC is its context.
// (Names in extensions never depend on self.)
// Lookup can propagate it up from, say a method to the enclosing type body.

// By default, propagate the selfDC up to a NomExt decl, body,
// or where clause
Optional<NullablePtr<DeclContext>>
AstScopeImpl::computeSelfDCForParent() const {
   return None;
}

// Forget the "self" declaration:
Optional<NullablePtr<DeclContext>>
GenericTypeOrExtensionScope::computeSelfDCForParent() const {
   return NullablePtr<DeclContext>();
}

Optional<NullablePtr<DeclContext>>
PatternEntryInitializerScope::computeSelfDCForParent() const {
   // Pattern binding initializers are only interesting insofar as they
   // affect lookup in an enclosing nominal type or extension thereof.
   if (auto *ic = getPatternEntry().getInitContext()) {
      if (auto *bindingInit = dyn_cast<PatternBindingInitializer>(ic)) {
         // Lazy variable initializer contexts have a 'self' parameter for
         // instance member lookup.
         if (bindingInit->getImplicitSelfDecl()) {
            return NullablePtr<DeclContext>(bindingInit);
         }
      }
   }
   return None;
}

Optional<NullablePtr<DeclContext>>
MethodBodyScope::computeSelfDCForParent() const {
   return NullablePtr<DeclContext>(decl);
}

#pragma mark ifUnknownIsCascadingUseAccordingTo

static bool isCascadingUseAccordingTo(const DeclContext *const dc) {
   return dc->isCascadingContextForLookup(false);
}

static bool ifUnknownIsCascadingUseAccordingTo(Optional<bool> isCascadingUse,
                                               const DeclContext *const dc) {
   return isCascadingUse.getValueOr(isCascadingUseAccordingTo(dc));
}

#pragma mark resolveIsCascadingUseForThisScope

Optional<bool> AstScopeImpl::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   return isCascadingUse;
}

Optional<bool> GenericParamScope::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   if (auto *dc = getDeclContext().getPtrOrNull())
      return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, dc);
   ast_scope_unreachable("generic what?");
}

Optional<bool> AbstractFunctionDeclScope::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   return decl->isCascadingContextForLookup(false) &&
          isCascadingUse.getValueOr(true);
}

Optional<bool> AbstractFunctionBodyScope::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   return false;
}

Optional<bool> GenericTypeOrExtensionScope::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   // Could override for ExtensionScope and just return true
   return ifUnknownIsCascadingUseAccordingTo(isCascadingUse,
                                             getDeclContext().get());
}

Optional<bool>
DefaultArgumentInitializerScope::resolveIsCascadingUseForThisScope(
   Optional<bool>) const {
   return false;
}

Optional<bool> ClosureParametersScope::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, closureExpr);
}
Optional<bool> ClosureBodyScope::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, closureExpr);
}

Optional<bool> PatternEntryInitializerScope::resolveIsCascadingUseForThisScope(
   Optional<bool> isCascadingUse) const {
   auto *const initContext = getPatternEntry().getInitContext();
   auto *PBI = cast_or_null<PatternBindingInitializer>(initContext);
   auto *isd = PBI ? PBI->getImplicitSelfDecl() : nullptr;

   // 'self' is available within the pattern initializer of a 'lazy' variable.
   if (isd)
      return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, PBI);

   // initializing stored property of a type
   auto *const patternDeclContext = decl->getDeclContext();
   if (patternDeclContext->isTypeContext())
      return isCascadingUseAccordingTo(PBI->getParent());

   // initializing global or local
   if (PBI)
      return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, PBI);

   return isCascadingUse;
}

bool isLocWithinAnInactiveClause(const SourceLoc loc, SourceFile *SF) {
   class InactiveClauseTester : public AstWalker {
      const SourceLoc loc;
      const SourceManager &SM;

   public:
      bool wasFoundWithinInactiveClause = false;

      InactiveClauseTester(const SourceLoc loc, const SourceManager &SM)
         : loc(loc), SM(SM) {}

      bool walkToDeclPre(Decl *D) override {
         if (const auto *ifc = dyn_cast<IfConfigDecl>(D)) {
            for (const auto &clause : ifc->getClauses()) {
               if (clause.isActive)
                  continue;
               for (const auto n : clause.Elements) {
                  SourceRange sr = n.getSourceRange();
                  if (sr.isValid() && SM.rangeContainsTokenLoc(sr, loc)) {
                     wasFoundWithinInactiveClause = true;
                     return false;
                  }
               }
            }
         }
         return AstWalker::walkToDeclPre(D);
      }
   };
   InactiveClauseTester tester(loc, SF->getAstContext().SourceMgr);
   SF->walk(tester);
   return tester.wasFoundWithinInactiveClause;
}
